/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.design;

import sagittarius.Debug;

import fv3.math.Matrix;

/**
 * Code common to {@link HyperbolicShell} and {@link HyperbolicWing}.
 *
 * @see Model
 */
public abstract class AbstractModel
    extends fv3.math.Abstract
    implements Model
{


    public AbstractModel(){
        super();
    }



    /**
     * Polygon area scaling function
     */
    protected float[] generatePath(final float a){
        Model.Path path = (Model.Path)this;

        final float[] modelVertices = path.getPathVertices();
        final float modelArea = path.getPathArea();

        if (EEQ(a,modelArea))
            return modelVertices;
        else {
            /*
             * Fit the model to the volume station area
             */
            final double scale = Math.sqrt((double)a/(double)modelArea);

            final int count = modelVertices.length;
            final float[] vertices = new float[count];

            for (int cc = 0; cc < count; cc++){

                vertices[cc] = (float)(modelVertices[cc] * scale);
            }
            return vertices;
        }
    }
    /**
     * Circle for radius function
     */
    protected float[] generateCircle(final float r){

        final double r2 = ((double)r*(double)r);

        if (Debug.Geometry){
            System.out.printf("[G/R/0] circle radius % 4.2f, area % 4.2f%n",r,(r2*Math.PI));
        }

        final int count = Model.Top;

        final int length = (count<<1);

        final float[] top = new float[length];

        final float[] bot = new float[length-2];

        final float[] V = new float[3];

        final double dx = (2.0*r)/(count-1);
        /*
         * Top
         */
        double x = r, y = 0;

        for (int cc = 0; cc < length; cc += 2){
            V[X] = (float)x;
            V[Y] = (float)y;
            V[Z] = 0f;

            if (Debug.Geometry)
                System.out.printf("[G/R/T] y % 4.2f, z % 4.2f%n",x,y);

            System.arraycopy(V,0,top,cc,2);

            x -= dx;
            y = +Math.sqrt(r2-(x*x));

            if (y != y)
                y = 0.0;
        }
        /*
         * Bottom
         */
        x = -r; y = 0.0;

        for (int cc = 0; cc < length; cc += 2){
            V[X] = (float)x;
            V[Y] = (float)y;
            V[Z] = 0f;

            if (0 < cc){

                if (Debug.Geometry)
                    System.out.printf("[G/R/B] y % 4.2f, z % 4.2f%n",x,y);

                System.arraycopy(V,0,bot,(cc-2),2);
            }

            x += dx;
            y = -Math.sqrt(r2-(x*x));

            if (y != y)
                y = 0.0;
        }
        return Cat(top,bot);
    }
    /**
     * Function used from the {@link HyperbolicShell} constructor
     */
    protected float[] generateShell(final float s){

        if (Debug.Geometry)
            System.out.printf("[G/S/0] hyperbola sector % 4.2f%n",s);

        final int count = Model.Top;

        final int length = (count<<1);

        final float[] top = new float[length];

        final float[] bot = new float[length-2];

        final float[] V = new float[3];

        final double a = 1.0/s;
        final double b = s;
        final double _b_a = (b-a);

        final double dx = (2.0*(b-1.0))/(count-1);

        final double _b_a_2 = (_b_a*_b_a);
        /*
         * hyperbola rotation matrix
         */
        final Matrix R = new Matrix();

        /*
         * Top
         */
        R.rotateZ(0.75f*PI);

        double x = a, y = b;

        for (int cc = 0; cc < length; cc += 2){
            V[X] = (float)x;
            V[Y] = (float)y;
            V[Z] = 0f;

            R.transform(V);

            System.arraycopy(V,0,top,cc,2);

            if (x < y){
                y -= dx;
                x = 1.0/y;
            }
            else {
                x += dx;
                y = 1.0/x;
            }
        }

        final float dy = -(top[Y]);

        for (int cc = 0; cc < length; cc++){
            x = top[cc++];
            y = top[cc]+dy;

            top[cc] = (float)y;

            if (Debug.Geometry)
                System.out.printf("[G/S/T] y % 4.2f, z % 4.2f%n",x,y);
        }

        R.identity();
        /*
         * Bottom
         */
        R.rotateZ(PI);

        for (int cc = 2; cc < length; cc += 2){
            V[X] = top[cc+X];
            V[Y] = top[cc+Y];
            V[Z] = 0f;

            R.transform(V);

            if (Debug.Geometry){
                x = V[X];
                y = V[Y];
                System.out.printf("[G/S/B] y % 4.2f, z % 4.2f%n",x,y);
            }
            System.arraycopy(V,0,bot,(cc-2),2);
        }

        return Cat(top,bot);
    }
}
